// This file is part of KWIVER, and is distributed under the
// OSI-approved BSD 3-Clause License. See top-level LICENSE file or
// https://github.com/Kitware/kwiver/blob/master/LICENSE for details.

/// \file
/// \brief Test KLV 0104 read / write.

#include "data_format.h"

#include <arrows/klv/klv_0104.h>
#include <arrows/klv/klv_packet.h>

// ----------------------------------------------------------------------------
int
main( int argc, char** argv )
{
  ::testing::InitGoogleTest( &argc, argv );
  return RUN_ALL_TESTS();
}

// ----------------------------------------------------------------------------
void
test_read_write( klv_value const& expected_result,
                 klv_bytes_t const& input_bytes )
{
  using format_t = klv_0104_universal_set_format;
  test_read_write_format< format_t >( expected_result, input_bytes );
}

// ----------------------------------------------------------------------------
klv_uds_key
to_key( klv_0104_tag tag )
{
  return klv_0104_traits_lookup().by_tag( tag ).uds_key();
}

auto const input_bytes = klv_bytes_t{
  // KLV_0104_DEVICE_DESIGNATION
  0x06, 0x0E, 0x2B, 0x34, 0x01, 0x01, 0x01, 0x01,
  0x01, 0x01, 0x20, 0x01, 0x00, 0x00, 0x00, 0x00,
  0x05,
  'M', 'Q', '1', '-', 'B',

  // KLV_0104_EPISODE_NUMBER
  0x06, 0x0E, 0x2B, 0x34, 0x01, 0x01, 0x01, 0x01,
  0x01, 0x05, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00,
  0x04,
  0x3F, 0x80, 0x00, 0x00,

  // KLV_0104_IMAGE_SOURCE_DEVICE
  0x06, 0x0E, 0x2B, 0x34, 0x01, 0x01, 0x01, 0x01,
  0x04, 0x20, 0x01, 0x02, 0x01, 0x01, 0x00, 0x00,
  0x02,
  'E', 'O',

  // KLV_0104_HORIZONTAL_FOV
  0x06, 0x0E, 0x2B, 0x34, 0x01, 0x01, 0x01, 0x02,
  0x04, 0x20, 0x02, 0x01, 0x01, 0x08, 0x00, 0x00,
  0x04,
  0x43, 0x10, 0x92, 0x41,

  // KLV_0104_VERTICAL_FOV
  0x06, 0x0E, 0x2B, 0x34, 0x01, 0x01, 0x01, 0x07,
  0x04, 0x20, 0x02, 0x01, 0x01, 0x0A, 0x01, 0x00,
  0x04,
  0x43, 0x18, 0xA4, 0xC5,

  // KLV_0104_IMAGE_COORDINATE_SYSTEM
  0x06, 0x0E, 0x2B, 0x34, 0x01, 0x01, 0x01, 0x01,
  0x07, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00,
  0x03,
  'U', 'T', 'M',

  // KLV_0104_DEVICE_ALTITUDE
  0x06, 0x0E, 0x2B, 0x34, 0x01, 0x01, 0x01, 0x01,
  0x07, 0x01, 0x02, 0x01, 0x02, 0x02, 0x00, 0x00,
  0x04,
  0x46, 0x5D, 0xBA, 0xE1,

  // KLV_0104_DEVICE_LATITUDE
  0x06, 0x0E, 0x2B, 0x34, 0x01, 0x01, 0x01, 0x03,
  0x07, 0x01, 0x02, 0x01, 0x02, 0x04, 0x02, 0x00,
  0x08,
  0x40, 0x4E, 0x16, 0xA2, 0x22, 0x8E, 0x2D, 0x44,

  // KLV_0104_DEVICE_LONGITUDE
  0x06, 0x0E, 0x2B, 0x34, 0x01, 0x01, 0x01, 0x03,
  0x07, 0x01, 0x02, 0x01, 0x02, 0x06, 0x02, 0x00,
  0x08,
  0x40, 0x60, 0x0D, 0xA8, 0x02, 0x94, 0x1B, 0x50,

  // KLV_0104_FRAME_CENTER_LATITUDE
  0x06, 0x0E, 0x2B, 0x34, 0x01, 0x01, 0x01, 0x01,
  0x07, 0x01, 0x02, 0x01, 0x03, 0x02, 0x00, 0x00,
  0x08,
  0xC0, 0x25, 0x15, 0xB3, 0xF6, 0x82, 0x2B, 0x68,

  // KLV_0104_FRAME_CENTER_LONGITUDE
  0x06, 0x0E, 0x2B, 0x34, 0x01, 0x01, 0x01, 0x01,
  0x07, 0x01, 0x02, 0x01, 0x03, 0x04, 0x00, 0x00,
  0x08,
  0x40, 0x3D, 0x28, 0x6B, 0x7C, 0xB2, 0x50, 0xD6,

  // KLV_0104_CORNER_LATITUDE_POINT_1
  0x06, 0x0E, 0x2B, 0x34, 0x01, 0x01, 0x01, 0x03,
  0x07, 0x01, 0x02, 0x01, 0x03, 0x07, 0x01, 0x00,
  0x08,
  0xC0, 0x25, 0x28, 0xC6, 0x4F, 0xDB, 0x09, 0xA6,

  // KLV_0104_CORNER_LATITUDE_POINT_2
  0x06, 0x0E, 0x2B, 0x34, 0x01, 0x01, 0x01, 0x03,
  0x07, 0x01, 0x02, 0x01, 0x03, 0x08, 0x01, 0x00,
  0x08,
  0xC0, 0x25, 0x21, 0xE2, 0x8D, 0xFF, 0x31, 0xA4,

  // KLV_0104_CORNER_LATITUDE_POINT_3
  0x06, 0x0E, 0x2B, 0x34, 0x01, 0x01, 0x01, 0x03,
  0x07, 0x01, 0x02, 0x01, 0x03, 0x09, 0x01, 0x00,
  0x08,
  0xC0, 0x25, 0x1A, 0xFF, 0x19, 0x50, 0x33, 0x1E,

  // KLV_0104_CORNER_LATITUDE_POINT_4
  0x06, 0x0E, 0x2B, 0x34, 0x01, 0x01, 0x01, 0x03,
  0x07, 0x01, 0x02, 0x01, 0x03, 0x0A, 0x01, 0x00,
  0x08,
  0xC0, 0x25, 0x14, 0x1B, 0x5A, 0xCF, 0x59, 0x6F,

  // KLV_0104_CORNER_LONGITUDE_POINT_1
  0x06, 0x0E, 0x2B, 0x34, 0x01, 0x01, 0x01, 0x03,
  0x07, 0x01, 0x02, 0x01, 0x03, 0x0B, 0x01, 0x00,
  0x08,
  0x40, 0x3D, 0x20, 0x9B, 0x2D, 0x17, 0x90, 0x3F,

  // KLV_0104_CORNER_LONGITUDE_POINT_2
  0x06, 0x0E, 0x2B, 0x34, 0x01, 0x01, 0x01, 0x03,
  0x07, 0x01, 0x02, 0x01, 0x03, 0x0C, 0x01, 0x00,
  0x08,
  0x40, 0x3D, 0x24, 0x0D, 0x0E, 0x05, 0x7C, 0x41,

  // KLV_0104_CORNER_LONGITUDE_POINT_3
  0x06, 0x0E, 0x2B, 0x34, 0x01, 0x01, 0x01, 0x03,
  0x07, 0x01, 0x02, 0x01, 0x03, 0x0D, 0x01, 0x00,
  0x08,
  0x40, 0x3D, 0x27, 0x7E, 0xC8, 0x5C, 0xFB, 0x84,

  // KLV_0104_CORNER_LONGITUDE_POINT_4
  0x06, 0x0E, 0x2B, 0x34, 0x01, 0x01, 0x01, 0x03,
  0x07, 0x01, 0x02, 0x01, 0x03, 0x0E, 0x01, 0x00,
  0x08,
  0x40, 0x3D, 0x2A, 0xF0, 0xA7, 0x9D, 0x68, 0x5B,

  // KLV_0104_FRAME_CENTER_ELEVATION
  0x06, 0x0E, 0x2B, 0x34, 0x01, 0x01, 0x01, 0x0A,
  0x07, 0x01, 0x02, 0x01, 0x03, 0x16, 0x00, 0x00,
  0x04,
  0x45, 0x49, 0x00, 0x98,

  // KLV_0104_SLANT_RANGE
  0x06, 0x0E, 0x2B, 0x34, 0x01, 0x01, 0x01, 0x01,
  0x07, 0x01, 0x08, 0x01, 0x01, 0x00, 0x00, 0x00,
  0x04,
  0x47, 0x85, 0xF7, 0x7E,

  // KLV_0104_TARGET_WIDTH
  0x06, 0x0E, 0x2B, 0x34, 0x01, 0x01, 0x01, 0x01,
  0x07, 0x01, 0x09, 0x02, 0x01, 0x00, 0x00, 0x00,
  0x04,
  0x44, 0x34, 0xB4, 0x79,

  // KLV_0104_SENSOR_ROLL_ANGLE
  0x06, 0x0E, 0x2B, 0x34, 0x01, 0x01, 0x01, 0x01,
  0x07, 0x01, 0x10, 0x01, 0x01, 0x00, 0x00, 0x00,
  0x04,
  0x43, 0x30, 0xDD, 0x8D,

  // KLV_0104_ANGLE_TO_NORTH
  0x06, 0x0E, 0x2B, 0x34, 0x01, 0x01, 0x01, 0x01,
  0x07, 0x01, 0x10, 0x01, 0x02, 0x00, 0x00, 0x00,
  0x04,
  0x43, 0x20, 0xB8, 0x1E,

  // KLV_0104_OBLIQUITY_ANGLE
  0x06, 0x0E, 0x2B, 0x34, 0x01, 0x01, 0x01, 0x01,
  0x07, 0x01, 0x10, 0x01, 0x03, 0x00, 0x00, 0x00,
  0x04,
  0x43, 0xAE, 0x65, 0x6B,

  // KLV_0104_PLATFORM_ROLL_ANGLE
  0x06, 0x0E, 0x2B, 0x34, 0x01, 0x01, 0x01, 0x07,
  0x07, 0x01, 0x10, 0x01, 0x04, 0x00, 0x00, 0x00,
  0x04,
  0x40, 0x59, 0xF9, 0xB4,

  // KLV_0104_PLATFORM_PITCH_ANGLE
  0x06, 0x0E, 0x2B, 0x34, 0x01, 0x01, 0x01, 0x07,
  0x07, 0x01, 0x10, 0x01, 0x05, 0x00, 0x00, 0x00,
  0x04,
  0xBE, 0xDC, 0xF1, 0xBA,

  // KLV_0104_PLATFORM_HEADING_ANGLE
  0x06, 0x0E, 0x2B, 0x34, 0x01, 0x01, 0x01, 0x07,
  0x07, 0x01, 0x10, 0x01, 0x06, 0x00, 0x00, 0x00,
  0x04,
  0x43, 0x1F, 0xF9, 0x70,

  // KLV_0104_USER_DEFINED_TIMESTAMP
  0x06, 0x0E, 0x2B, 0x34, 0x01, 0x01, 0x01, 0x03,
  0x07, 0x02, 0x01, 0x01, 0x01, 0x05, 0x00, 0x00,
  0x08,
  0x00, 0x04, 0x59, 0xF4, 0xA6, 0xAA, 0x4A, 0xA8,

  // KLV_0104_START_DATETIME
  0x06, 0x0E, 0x2B, 0x34, 0x01, 0x01, 0x01, 0x01,
  0x07, 0x02, 0x01, 0x02, 0x01, 0x01, 0x00, 0x00,
  0x0F,
  '1', '9', '9', '5', '0', '4', '1', '6', 'T', '1', '3', '4', '4', '5', '4',

  // KLV_0104_EVENT_START_DATETIME
  0x06, 0x0E, 0x2B, 0x34, 0x01, 0x01, 0x01, 0x01,
  0x07, 0x02, 0x01, 0x02, 0x07, 0x01, 0x00, 0x00,
  0x0F,
  '1', '9', '9', '5', '0', '4', '1', '6', 'T', '1', '3', '4', '4', '5', '4',

  // KLV_0104_SECURITY_LOCAL_SET
  0x06, 0x0E, 0x2B, 0x34, 0x02, 0x03, 0x01, 0x01,
  0x0E, 0x01, 0x03, 0x03, 0x02, 0x00, 0x00, 0x00,
  0x00, };

auto const expected_result = klv_universal_set{
  { to_key( KLV_0104_FRAME_CENTER_LATITUDE ),   -10.542388633146132 },
  { to_key( KLV_0104_FRAME_CENTER_LONGITUDE ),   29.157890122923014 },
  { to_key( KLV_0104_FRAME_CENTER_ELEVATION ),  double{  3216.03723f } },
  { to_key( KLV_0104_IMAGE_COORDINATE_SYSTEM ), std::string{ "UTM" } },
  { to_key( KLV_0104_TARGET_WIDTH ),            double{  722.819867f } },
  { to_key( KLV_0104_START_DATETIME ),
    std::string{ "19950416T134454" } },
  { to_key( KLV_0104_EVENT_START_DATETIME ),
    std::string{ "19950416T134454" } },
  { to_key( KLV_0104_USER_DEFINED_TIMESTAMP ),
    uint64_t{ 0x000459F4A6AA4AA8 } },
  { to_key( KLV_0104_CORNER_LATITUDE_POINT_1 ), -10.5796380 },
  { to_key( KLV_0104_CORNER_LATITUDE_POINT_2 ), -10.5661816 },
  { to_key( KLV_0104_CORNER_LATITUDE_POINT_3 ), -10.5527275 },
  { to_key( KLV_0104_CORNER_LATITUDE_POINT_4 ), -10.5392712 },
  { to_key( KLV_0104_CORNER_LONGITUDE_POINT_1 ), 29.1273678 },
  { to_key( KLV_0104_CORNER_LONGITUDE_POINT_2 ), 29.1408242 },
  { to_key( KLV_0104_CORNER_LONGITUDE_POINT_3 ), 29.1542783 },
  { to_key( KLV_0104_CORNER_LONGITUDE_POINT_4 ), 29.1677346 },
  { to_key( KLV_0104_SLANT_RANGE ),              double{  68590.9832f } },
  { to_key( KLV_0104_SENSOR_ROLL_ANGLE ),        double{  176.865437f } },
  { to_key( KLV_0104_ANGLE_TO_NORTH ),           double{  160.719211f } },
  { to_key( KLV_0104_OBLIQUITY_ANGLE ),          double{  348.792324f } },
  { to_key( KLV_0104_PLATFORM_ROLL_ANGLE ),      double{  3.40586566f } },
  { to_key( KLV_0104_PLATFORM_PITCH_ANGLE ),     double{ -0.431531724f } },
  { to_key( KLV_0104_PLATFORM_HEADING_ANGLE ),   double{  159.974365f } },
  { to_key( KLV_0104_HORIZONTAL_FOV ),           double{  144.571298f } },
  { to_key( KLV_0104_VERTICAL_FOV ),             double{  152.643626f } },
  { to_key( KLV_0104_DEVICE_ALTITUDE ),          double{  14190.7195f } },
  { to_key( KLV_0104_DEVICE_LATITUDE ),           60.176822966978335 },
  { to_key( KLV_0104_DEVICE_LONGITUDE ),          128.42675904204452 },
  { to_key( KLV_0104_IMAGE_SOURCE_DEVICE ),      std::string{ "EO" } },
  { to_key( KLV_0104_EPISODE_NUMBER ),           double{  1.0f } },
  { to_key( KLV_0104_DEVICE_DESIGNATION ),       std::string{ "MQ1-B" } },
  { to_key( KLV_0104_SECURITY_LOCAL_SET ),       {} }, };

// ----------------------------------------------------------------------------
TEST ( klv, read_write_0104 )
{
  CALL_TEST( test_read_write, {}, {} );
  CALL_TEST( test_read_write, expected_result, input_bytes );
}

// ----------------------------------------------------------------------------
TEST ( klv, read_write_0104_packet )
{
  auto const packet_header = klv_bytes_t{
    0x06, 0x0E, 0x2B, 0x34, 0x02, 0x01, 0x01, 0x01,
    0x0E, 0x01, 0x01, 0x02, 0x01, 0x01, 0x00, 0x00,
    0x82, 0x02, 0xE4 };
  auto const packet_footer = klv_bytes_t{};

  // Assemble the target packet's serialized form
  auto packet_bytes =
    klv_bytes_t( packet_header.size() +
                 input_bytes.size() +
                 packet_footer.size() );
  auto bytes_it = packet_bytes.begin();
  bytes_it = std::copy( packet_header.cbegin(), packet_header.cend(),
                        bytes_it );
  bytes_it = std::copy( input_bytes.cbegin(), input_bytes.cend(),
                        bytes_it );
  bytes_it = std::copy( packet_footer.cbegin(), packet_footer.cend(),
                        bytes_it );

  // Assemble the target packet's unserialized form
  auto const test_packet = klv_packet{ klv_0104_key(), expected_result };

  // Deserialize
  auto read_it = packet_bytes.cbegin();
  auto const read_packet = klv_read_packet( read_it, packet_bytes.size() );
  EXPECT_EQ( packet_bytes.cend(), read_it );
  EXPECT_EQ( test_packet, read_packet );

  // Reserialize
  klv_bytes_t written_bytes( klv_packet_length( read_packet ) );
  auto write_it = written_bytes.begin();
  klv_write_packet( read_packet, write_it, written_bytes.size() );
  EXPECT_EQ( written_bytes.end(), write_it );
  EXPECT_EQ( packet_bytes, written_bytes );
}
